
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import utils.ByteArrayUtil;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;


/**
 * EasyCache 支持通过HTTP接口进行数据的获取和缓存。
 */
public class EasyCache {
    // 使用读写锁来控制对groups集合的并发访问，确保线程安全。
    private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
    // 使用互斥锁来保护groups集合在创建新组时的线程安全。
    private final Lock lock = new ReentrantLock();
    // 存储缓存组，每个组对应一个特定的缓存策略。
    private final Map<String, Group> groups = new HashMap<>();

    /**
     * 创建一个新的缓存组。
     * @param name 缓存组的名称。
     * @param size 缓存组的大小。
     * @param getter 用于获取缓存数据的函数。
     * @return 创建的缓存组对象。
     * @throws IllegalArgumentException 如果getter为null，则抛出异常。
     */
    public Group newGroup(String name, int size, Function<String, byte[]> getter) {
        if (getter == null) {
            throw new IllegalArgumentException("null getter");
        }
        lock.lock(); // 锁定互斥锁以保护下面的代码块。
        Group g = new Group(name, getter, size); // 创建一个新的缓存组。
        groups.put(name, g); // 将新创建的缓存组添加到groups集合中。
        lock.unlock(); // 解锁互斥锁。
        return g;
    }

    /**
     * 根据名称获取缓存组。
     * @param name 缓存组的名称。
     * @return 对应的缓存组对象，如果不存在则返回null。
     */
    public Group getGroup(String name) {
        rwlock.readLock().lock(); // 锁定读锁以安全地读取groups集合。
        Group g = groups.get(name); // 从groups集合中获取缓存组。
        rwlock.readLock().unlock(); // 解锁读锁。
        return g;
    }

    /**
     * 启动一个HTTP服务来处理缓存请求。
     * @param ip 服务绑定的IP地址。
     * @param port 服务绑定的端口号。
     */
    public void serve(String ip, int port) {
        HttpPool httpPool = new HttpPool(port, ip, this); // 创建HTTP连接池。
        new Thread(() -> { // 创建新线程来运行HTTP服务。
            try {
                httpPool.serve(); // 启动HTTP服务。
            } catch (IOException e) {
                e.printStackTrace(); // 打印异常堆栈信息。
            }
        }).start(); // 启动线程。
    }

    /**
     * 启动缓存服务器，用于处理缓存数据的同步和分发。
     * @param peerKey 缓存服务器的地址，格式为"IP:Port"。
     * @param peersAddr 其他缓存服务器的地址数组。
     * @param groupName 缓存组的名称。
     */
    public void startCacheServer(String peerKey, String[] peersAddr, String groupName) throws NoSuchAlgorithmException {
        Group g = this.getGroup(groupName); // 获取缓存组。
        String[] tmp = peerKey.split(":"); // 分割IP和端口。
        String ip = tmp[0]; // 获取IP地址。
        int port = Integer.parseInt(tmp[1]); // 获取端口号。
        HttpPool peers = new HttpPool(port, ip, this); // 创建HTTP连接池。
        peers.set(peersAddr); // 设置其他缓存服务器的地址。
        g.registerPeers(peers); // 注册其他缓存服务器。
        System.out.println("easyCache is running at " + peerKey); // 打印启动信息。
        new Thread(() -> { // 创建新线程来运行缓存服务器。
            try {
                peers.serve(); // 启动缓存服务器。
            } catch (IOException e) {
                e.printStackTrace(); // 打印异常堆栈信息。
            }
        }).start(); // 启动线程。
    }

    /**
     * 启动API服务器，提供HTTP接口访问缓存数据。
     * @param apiAddr API服务器的地址，格式为"IP:Port"。
     * @param peersAddr 其他缓存服务器的地址数组。
     * @param groupName 缓存组的名称。
     */
    public void startAPIServer(String apiAddr, String[] peersAddr, String groupName) throws NoSuchAlgorithmException {
        Group g = this.getGroup(groupName); // 获取缓存组。
        String[] tmp = apiAddr.split(":"); // 分割IP和端口。
        String ip = tmp[0]; // 获取IP地址。
        int port = Integer.parseInt(tmp[1]); // 获取端口号。
        HttpPool peers = new HttpPool(port, ip, this); // 创建HTTP连接池。
        peers.set(peersAddr); // 设置其他缓存服务器的地址。
        g.registerPeers(peers); // 注册其他缓存服务器。
        HttpServer httpServer = null; // 创建HTTP服务器。
        System.out.println("api server start at " + apiAddr); // 打印启动信息。
        try {
            httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getByName(ip), port), 0); // 创建HTTP服务器。
        } catch (IOException e) {
            e.printStackTrace(); // 打印异常堆栈信息。
        }
        httpServer.createContext("/api", new HttpHandler() { // 创建HTTP处理器。
            @Override
            public void handle(HttpExchange httpExchange) throws IOException {
                URI uri = httpExchange.getRequestURI(); // 获取请求URI。
                String path = uri.getPath(); // 获取请求路径。
                String key = path.split("/")[2]; // 获取缓存键。
                byte[] respContents = ByteArrayUtil.bToO(g.get(key)).toString().getBytes("UTF-8"); // 获取缓存数据并转换为字节数组。
                httpExchange.getResponseHeaders().add("Content-Type", "text/html; charset=UTF-8"); // 设置响应头。
                httpExchange.sendResponseHeaders(200, respContents.length); // 发送响应头和状态码。
                httpExchange.getResponseBody().write(respContents); // 写入响应体。
                httpExchange.close(); // 关闭HTTP交换。
            }
        });
        httpServer.start(); // 启动HTTP服务器。
    }
}
